#ifndef __STREAM_H__
#define __STREAM_H__

#include "nonecopyable.h"
#include <stdint.h>
#include <stddef.h>


/// @brief stream
class Stream: public NoneCopyable
{
public:
    explicit Stream();
    virtual ~Stream();

public:
    virtual int available() = 0;
    virtual int read() = 0;
    virtual size_t write(uint8_t c) = 0;
    
    virtual int peek(){return -1;}

    // parsing methods

    void setTimeout(unsigned long timeout);  // sets maximum milliseconds to wait for stream data, default is 1 second
    unsigned long getTimeout () const { return _timeout; }

    bool find(const char *target);   // reads data from the stream until the target string is found
    bool find(uint8_t *target) {
        return find((char *) target);
    }
    // returns true if target string is found, false if timed out (see setTimeout)

    bool find(const char *target, size_t length);   // reads data from the stream until the target string of given length is found
    bool find(const uint8_t *target, size_t length) {
        return find((char *) target, length);
    }
    // returns true if target string is found, false if timed out

    bool find(char target) { return find (&target, 1); }

    bool findUntil(const char *target, const char *terminator);   // as find but search ends if the terminator string is found
    bool findUntil(const uint8_t *target, const char *terminator) {
        return findUntil((char *) target, terminator);
    }

    bool findUntil(const char *target, size_t targetLen, const char *terminate, size_t termLen);   // as above but search ends if the terminate string is found
    bool findUntil(const uint8_t *target, size_t targetLen, const char *terminate, size_t termLen) {
        return findUntil((char *) target, targetLen, terminate, termLen);
    }

    virtual size_t readBytes(char *buffer, size_t length); // read chars from stream into buffer
    virtual size_t readBytes(uint8_t *buffer, size_t length) {
        return readBytes((char *) buffer, length);
    }
    // terminates if length characters have been read or timeout (see setTimeout)
    // returns the number of characters placed in the buffer (0 means no valid data found)

    size_t readBytesUntil(char terminator, char *buffer, size_t length); // as readBytes with terminator character
    size_t readBytesUntil(char terminator, uint8_t *buffer, size_t length) {
        return readBytesUntil(terminator, (char *) buffer, length);
    }
    // terminates if length characters have been read, timeout, or if the terminator character  detected
    // returns the number of characters placed in the buffer (0 means no valid data found)

    // Arduino String functions to be added here
    virtual std::string readString();
    std::string readStringUntil(char terminator);

    virtual int read (uint8_t* buffer, size_t len);
    int read (char* buffer, size_t len) { return read((uint8_t*)buffer, len); }

    //////////////////// extension: direct access to input buffer
    // to provide when possible a pointer to available data for read

    // informs user and ::to*() on effective buffered peek API implementation
    // by default: not available
    virtual bool hasPeekBufferAPI () const { return false; }

    // returns number of byte accessible by peekBuffer()
    virtual size_t peekAvailable () { return 0; }

    // returns a pointer to available data buffer (size = peekAvailable())
    // semantic forbids any kind of ::read()
    //     - after calling peekBuffer()
    //     - and before calling peekConsume()
    virtual const char* peekBuffer () { return nullptr; }

    // consumes bytes after peekBuffer() use
    // (then ::read() is allowed)
    virtual void peekConsume (size_t consume) { (void)consume; }

    // by default read timeout is possible (incoming data from network,serial..)
    // children can override to false (like String::)
    virtual bool inputCanTimeout () { return true; }

    size_t write(const char *str) {
        if(str == NULL)
            return 0;
        return write((const uint8_t *) str, strlen(str));
    }
    virtual size_t write(const uint8_t *buffer, size_t size);
    size_t write(const char *buffer, size_t size) {
        return write((const uint8_t *) buffer, size);
    }

    // default to zero, meaning "a single write may block"
    // should be overridden by subclasses with buffering
    virtual int availableForWrite() { return 0; }

    enum class Report
    {
        Success = 0,
        TimedOut,
        ReadError,
        WriteError,
        ShortOperation,
    };

    Report getLastSendReport () const { return _sendReport; }

protected:
    /** Acquire exclusive access to this object.
    */
    virtual void lock()
    {
        // Stub
    }

    /** Release exclusive access to this object.
    */
    virtual void unlock()
    {
        // Stub
    }

protected:
    unsigned long _timeout = 1000;  // number of milliseconds to wait for the next char before aborting timed read
    unsigned long _startMillis;  // used for timeout measurement
    
    int timedRead();    // private method to read stream with timeout

protected:
    void setReport (Report report) { _sendReport = report; }

private:
    Report _sendReport = Report::Success;

//////////////////// end of extensions

protected:
    long parseInt(char skipChar); // as parseInt() but the given skipChar is ignored
    // this allows format characters (typically commas) in values to be ignored

    float parseFloat(char skipChar);  // as parseFloat() but the given skipChar is ignored
};

#endif // __STREAM_H__
